#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_bt.h"

#include "esp_blufi_api.h"

#include "mbedtls/aes.h"
#include "mbedtls/dhm.h"
#include "mbedtls/md5.h"
#include "esp_crc.h"
#include "esp_err.h"
#include "esp_blufi.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "luat_base.h"

#define BLUFI_TAG "BLUFI"
#define BLUFI_INFO(fmt, ...)   ESP_LOGI(BLUFI_TAG, fmt, ##__VA_ARGS__)
#define BLUFI_ERROR(fmt, ...)  ESP_LOGE(BLUFI_TAG, fmt, ##__VA_ARGS__)

lua_State *GL;

/*
   The SEC_TYPE_xxx is for self-defined packet data type in the procedure of "BLUFI negotiate key"
   If user use other negotiation procedure to exchange(or generate) key, should redefine the type by yourself.
 */
#define SEC_TYPE_DH_PARAM_LEN   0x00
#define SEC_TYPE_DH_PARAM_DATA  0x01
#define SEC_TYPE_DH_P           0x02
#define SEC_TYPE_DH_G           0x03
#define SEC_TYPE_DH_PUBLIC      0x04


struct blufi_security
{
#define DH_SELF_PUB_KEY_LEN     128
#define DH_SELF_PUB_KEY_BIT_LEN (DH_SELF_PUB_KEY_LEN * 8)
	uint8_t self_public_key[DH_SELF_PUB_KEY_LEN];
#define SHARE_KEY_LEN           128
#define SHARE_KEY_BIT_LEN       (SHARE_KEY_LEN * 8)
	uint8_t share_key[SHARE_KEY_LEN];
	size_t share_len;
#define PSK_LEN                 16
	uint8_t psk[PSK_LEN];
	uint8_t *dh_param;
	int dh_param_len;
	uint8_t iv[16];
	mbedtls_dhm_context dhm;
	mbedtls_aes_context aes;
};
static struct blufi_security *blufi_sec;

static int my_rand(void *rng_state, unsigned char *output, size_t len)
{
	esp_fill_random(output, len);
	return (0);
}

extern void btc_blufi_report_error(esp_blufi_error_state_t state);

void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free)
{
	int ret;
	uint8_t type = data[0];

	if (blufi_sec == NULL)
	{
		BLUFI_ERROR("BLUFI Security is not initialized");
		btc_blufi_report_error(ESP_BLUFI_INIT_SECURITY_ERROR);
		return;
	}

	switch (type)
	{
		case SEC_TYPE_DH_PARAM_LEN:
			blufi_sec->dh_param_len = ((data[1] << 8) | data[2]);
			if (blufi_sec->dh_param)
			{
				free(blufi_sec->dh_param);
				blufi_sec->dh_param = NULL;
			}
			blufi_sec->dh_param = (uint8_t *) malloc(blufi_sec->dh_param_len);
			if (blufi_sec->dh_param == NULL)
			{
				btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
				BLUFI_ERROR("%s, malloc failed\n", __func__);
				return;
			}
			break;
		case SEC_TYPE_DH_PARAM_DATA:
		{
			if (blufi_sec->dh_param == NULL)
			{
				BLUFI_ERROR("%s, blufi_sec->dh_param == NULL\n", __func__);
				btc_blufi_report_error(ESP_BLUFI_DH_PARAM_ERROR);
				return;
			}
			uint8_t *param = blufi_sec->dh_param;
			memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len);
			ret = mbedtls_dhm_read_params(&blufi_sec->dhm, &param, &param[blufi_sec->dh_param_len]);
			if (ret)
			{
				BLUFI_ERROR("%s read param failed %d\n", __func__, ret);
				btc_blufi_report_error(ESP_BLUFI_READ_PARAM_ERROR);
				return;
			}
			free(blufi_sec->dh_param);
			blufi_sec->dh_param = NULL;
			ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size(&blufi_sec->dhm.P),
			                              blufi_sec->self_public_key, blufi_sec->dhm.len, my_rand, NULL);
			if (ret)
			{
				BLUFI_ERROR("%s make public failed %d\n", __func__, ret);
				btc_blufi_report_error(ESP_BLUFI_MAKE_PUBLIC_ERROR);
				return;
			}

			mbedtls_dhm_calc_secret(&blufi_sec->dhm,
			                        blufi_sec->share_key,
			                        SHARE_KEY_BIT_LEN,
			                        &blufi_sec->share_len,
			                        NULL, NULL);

			mbedtls_md5(blufi_sec->share_key, blufi_sec->share_len, blufi_sec->psk);

			mbedtls_aes_setkey_enc(&blufi_sec->aes, blufi_sec->psk, 128);

			/* alloc output data */
			*output_data = &blufi_sec->self_public_key[0];
			*output_len = blufi_sec->dhm.len;
			*need_free = false;

		}
			break;
		case SEC_TYPE_DH_P:
			break;
		case SEC_TYPE_DH_G:
			break;
		case SEC_TYPE_DH_PUBLIC:
			break;
	}
}

int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len)
{
	int ret;
	size_t iv_offset = 0;
	uint8_t iv0[16];

	memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv));
	iv0[0] = iv8;   /* set iv8 as the iv0[0] */

	ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_ENCRYPT, crypt_len, &iv_offset, iv0, crypt_data,
	                               crypt_data);
	if (ret)
	{
		return -1;
	}

	return crypt_len;
}

int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len)
{
	int ret;
	size_t iv_offset = 0;
	uint8_t iv0[16];

	memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv));
	iv0[0] = iv8;   /* set iv8 as the iv0[0] */

	ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_DECRYPT, crypt_len, &iv_offset, iv0, crypt_data,
	                               crypt_data);
	if (ret)
	{
		return -1;
	}

	return crypt_len;
}

uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len)
{
	/* This iv8 ignore, not used */
	return esp_crc16_be(0, data, len);
}

esp_err_t blufi_security_init(void)
{
	blufi_sec = (struct blufi_security *) malloc(sizeof(struct blufi_security));
	if (blufi_sec == NULL)
	{
		return ESP_FAIL;
	}

	memset(blufi_sec, 0x0, sizeof(struct blufi_security));

	mbedtls_dhm_init(&blufi_sec->dhm);
	mbedtls_aes_init(&blufi_sec->aes);

	memset(blufi_sec->iv, 0x0, 16);
	return 0;
}

void blufi_security_deinit(void)
{
	if (blufi_sec == NULL)
	{
		return;
	}
	if (blufi_sec->dh_param)
	{
		free(blufi_sec->dh_param);
		blufi_sec->dh_param = NULL;
	}
	mbedtls_dhm_free(&blufi_sec->dhm);
	mbedtls_aes_free(&blufi_sec->aes);

	memset(blufi_sec, 0x0, sizeof(struct blufi_security));

	free(blufi_sec);
	blufi_sec = NULL;
}

esp_err_t esp_blufi_host_init(void)
{
	int ret;
	ret = esp_bluedroid_init();
	if (ret)
	{
		BLUFI_ERROR("%s init bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
		return ESP_FAIL;
	}

	ret = esp_bluedroid_enable();
	if (ret)
	{
		BLUFI_ERROR("%s init bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
		return ESP_FAIL;
	}
	BLUFI_INFO("BD ADDR: "ESP_BD_ADDR_STR"\n", ESP_BD_ADDR_HEX(esp_bt_dev_get_address()));

	return ESP_OK;

}

esp_err_t esp_blufi_gap_register_callback(void)
{
	int rc;
	rc = esp_ble_gap_register_callback(esp_blufi_gap_event_handler);
	if (rc)
	{
		return rc;
	}
	return esp_blufi_profile_init();
}

esp_err_t esp_blufi_host_and_cb_init(esp_blufi_callbacks_t *callbacks)
{
	esp_err_t ret = ESP_OK;

	ret = esp_blufi_host_init();
	if (ret)
	{
		BLUFI_ERROR("%s initialise host failed: %s\n", __func__, esp_err_to_name(ret));
		return ret;
	}

	ret = esp_blufi_register_callbacks(callbacks);
	if (ret)
	{
		BLUFI_ERROR("%s blufi register failed, error code = %x\n", __func__, ret);
		return ret;
	}

	ret = esp_blufi_gap_register_callback();
	if (ret)
	{
		BLUFI_ERROR("%s gap register failed, error code = %x\n", __func__, ret);
		return ret;
	}

	return ESP_OK;

}

static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);

static wifi_config_t sta_config;
static wifi_config_t ap_config;

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
const int CONNECTED_BIT = BIT0;

/* store the station info for send back to phone */
static bool gl_sta_connected = false;
static bool ble_is_connected = false;
static uint8_t gl_sta_bssid[6];
static uint8_t gl_sta_ssid[32];
static int gl_sta_ssid_len;

static void ip_event_handler(void *arg, esp_event_base_t event_base,
                             int32_t event_id, void *event_data)
{
	wifi_mode_t mode;
	switch (event_id)
	{
		case IP_EVENT_STA_GOT_IP:
		{
			esp_blufi_extra_info_t info;

			xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
			esp_wifi_get_mode(&mode);

			memset(&info, 0, sizeof(esp_blufi_extra_info_t));
			memcpy(info.sta_bssid, gl_sta_bssid, 6);
			info.sta_bssid_set = true;
			info.sta_ssid = gl_sta_ssid;
			info.sta_ssid_len = gl_sta_ssid_len;
			if (ble_is_connected == true)
			{
				esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
			} else
			{
				BLUFI_INFO("BLUFI BLE is not connected yet\n");
			}
			lua_getglobal(GL, "sys_pub");
			lua_pushstring(GL,  "IP_READY");
			lua_call(GL, 1, 0);
			break;
		}
		default:
			break;
	}
}

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
	wifi_event_sta_connected_t *event;
	wifi_mode_t mode;

	switch (event_id)
	{
		case WIFI_EVENT_STA_START:
			esp_wifi_connect();
			break;
		case WIFI_EVENT_STA_CONNECTED:
			gl_sta_connected = true;
			event = (wifi_event_sta_connected_t *) event_data;
			memcpy(gl_sta_bssid, event->bssid, 6);
			memcpy(gl_sta_ssid, event->ssid, event->ssid_len);
			gl_sta_ssid_len = event->ssid_len;
			break;
		case WIFI_EVENT_STA_DISCONNECTED:
			/* This is a workaround as ESP32 WiFi libs don't currently
			   auto-reassociate. */
			gl_sta_connected = false;
			memset(gl_sta_ssid, 0, 32);
			memset(gl_sta_bssid, 0, 6);
			gl_sta_ssid_len = 0;
			esp_wifi_connect();
			xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
			break;
		case WIFI_EVENT_AP_START:
			esp_wifi_get_mode(&mode);

			/* TODO: get config or information of softap, then set to report extra_info */
			if (ble_is_connected == true)
			{
				if (gl_sta_connected)
				{
					esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL);
				} else
				{
					esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
				}
			} else
			{
				BLUFI_INFO("BLUFI BLE is not connected yet\n");
			}
			break;
		case WIFI_EVENT_SCAN_DONE:
		{
			uint16_t apCount = 0;
			esp_wifi_scan_get_ap_num(&apCount);
			if (apCount == 0)
			{
				BLUFI_INFO("Nothing AP found");
				break;
			}
			wifi_ap_record_t *ap_list = (wifi_ap_record_t *) malloc(sizeof(wifi_ap_record_t) * apCount);
			if (!ap_list)
			{
				BLUFI_ERROR("malloc error, ap_list is NULL");
				break;
			}
			ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list));
			esp_blufi_ap_record_t *blufi_ap_list = (esp_blufi_ap_record_t *) malloc(
					apCount * sizeof(esp_blufi_ap_record_t));
			if (!blufi_ap_list)
			{
				if (ap_list)
				{
					free(ap_list);
				}
				BLUFI_ERROR("malloc error, blufi_ap_list is NULL");
				break;
			}
			for (int i = 0; i < apCount; ++i)
			{
				blufi_ap_list[i].rssi = ap_list[i].rssi;
				memcpy(blufi_ap_list[i].ssid, ap_list[i].ssid, sizeof(ap_list[i].ssid));
			}

			if (ble_is_connected == true)
			{
				esp_blufi_send_wifi_list(apCount, blufi_ap_list);
			} else
			{
				BLUFI_INFO("BLUFI BLE is not connected yet\n");
			}

			esp_wifi_scan_stop();
			free(ap_list);
			free(blufi_ap_list);
			break;
		}
		default:
			break;
	}
}

static void initialise_wifi(void)
{
	ESP_ERROR_CHECK(esp_netif_init());
	wifi_event_group = xEventGroupCreate();
	ESP_ERROR_CHECK(esp_event_loop_create_default());
	esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
	assert(sta_netif);
	ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
	ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));

	wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
	ESP_ERROR_CHECK(esp_wifi_init(&cfg));
	ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
	ESP_ERROR_CHECK(esp_wifi_start());
}

static esp_blufi_callbacks_t example_callbacks = {
		.event_cb = blufi_event_callback,
		.negotiate_data_handler = blufi_dh_negotiate_data_handler,
		.encrypt_func = blufi_aes_encrypt,
		.decrypt_func = blufi_aes_decrypt,
		.checksum_func = blufi_crc_checksum,
};
static uint8_t blufi_service_uuid128[32] = {
		/* LSB <--------------------------------------------------------------------------------> MSB */
		//first uuid, 16bit, [12],[13] is the value
		0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
};
static esp_ble_adv_data_t blufi_adv_data = {
		.set_scan_rsp = false,
		.include_name = true,
		.include_txpower = true,
		.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
		.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
		.appearance = 0x00,
		.manufacturer_len = 0,
		.p_manufacturer_data =  NULL,
		.service_data_len = 0,
		.p_service_data = NULL,
		.service_uuid_len = 16,
		.p_service_uuid = blufi_service_uuid128,
		.flag = 0x6,
};

static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
{
	/* actually, should post to blufi_task handle the procedure,
	 * now, as a example, we do it more simply */
	switch (event)
	{
		case ESP_BLUFI_EVENT_INIT_FINISH:
			BLUFI_INFO("BLUFI init finish\n");
//			esp_blufi_adv_start();
			esp_ble_gap_set_device_name("Blufi_S3");
			esp_ble_gap_config_adv_data(&blufi_adv_data);
			break;
		case ESP_BLUFI_EVENT_DEINIT_FINISH:
			BLUFI_INFO("BLUFI deinit finish\n");
			break;
		case ESP_BLUFI_EVENT_BLE_CONNECT:
			BLUFI_INFO("BLUFI ble connect\n");
			ble_is_connected = true;
			esp_blufi_adv_stop();
			blufi_security_init();
			break;
		case ESP_BLUFI_EVENT_BLE_DISCONNECT:
			BLUFI_INFO("BLUFI ble disconnect\n");
			ble_is_connected = false;
			blufi_security_deinit();
			esp_blufi_adv_start();
			break;
		case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
			BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode);
			ESP_ERROR_CHECK(esp_wifi_set_mode(param->wifi_mode.op_mode));
			break;
		case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
			BLUFI_INFO("BLUFI requset wifi connect to AP\n");
			/* there is no wifi callback when the device has already connected to this wifi
			so disconnect wifi before connection.
			*/
			esp_wifi_disconnect();
			esp_wifi_connect();
			break;
		case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
			BLUFI_INFO("BLUFI requset wifi disconnect from AP\n");
			esp_wifi_disconnect();
			break;
		case ESP_BLUFI_EVENT_REPORT_ERROR:
			BLUFI_ERROR("BLUFI report error, error code %d\n", param->report_error.state);
			esp_blufi_send_error_info(param->report_error.state);
			break;
		case ESP_BLUFI_EVENT_GET_WIFI_STATUS:
		{
			wifi_mode_t mode;
			esp_blufi_extra_info_t info;

			esp_wifi_get_mode(&mode);

			if (gl_sta_connected)
			{
				memset(&info, 0, sizeof(esp_blufi_extra_info_t));
				memcpy(info.sta_bssid, gl_sta_bssid, 6);
				info.sta_bssid_set = true;
				info.sta_ssid = gl_sta_ssid;
				info.sta_ssid_len = gl_sta_ssid_len;
				esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
			} else
			{
				esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
			}
			BLUFI_INFO("BLUFI get wifi status from AP\n");

			break;
		}
		case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
			BLUFI_INFO("blufi close a gatt connection");
			esp_blufi_disconnect();
			break;
		case ESP_BLUFI_EVENT_RECV_STA_BSSID:
			memcpy(sta_config.sta.bssid, param->sta_bssid.bssid, 6);
			sta_config.sta.bssid_set = 1;
			esp_wifi_set_config(WIFI_IF_STA, &sta_config);
			BLUFI_INFO("Recv STA BSSID %s\n", sta_config.sta.ssid);
			break;
		case ESP_BLUFI_EVENT_RECV_STA_SSID:
			strncpy((char *) sta_config.sta.ssid, (char *) param->sta_ssid.ssid, param->sta_ssid.ssid_len);
			sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0';
			esp_wifi_set_config(WIFI_IF_STA, &sta_config);
			BLUFI_INFO("Recv STA SSID %s\n", sta_config.sta.ssid);
			break;
		case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
			strncpy((char *) sta_config.sta.password, (char *) param->sta_passwd.passwd, param->sta_passwd.passwd_len);
			sta_config.sta.password[param->sta_passwd.passwd_len] = '\0';
			esp_wifi_set_config(WIFI_IF_STA, &sta_config);
			BLUFI_INFO("Recv STA PASSWORD %s\n", sta_config.sta.password);
			break;
		case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
			strncpy((char *) ap_config.ap.ssid, (char *) param->softap_ssid.ssid, param->softap_ssid.ssid_len);
			ap_config.ap.ssid[param->softap_ssid.ssid_len] = '\0';
			ap_config.ap.ssid_len = param->softap_ssid.ssid_len;
			esp_wifi_set_config(WIFI_IF_AP, &ap_config);
			BLUFI_INFO("Recv SOFTAP SSID %s, ssid len %d\n", ap_config.ap.ssid, ap_config.ap.ssid_len);
			break;
		case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
			strncpy((char *) ap_config.ap.password, (char *) param->softap_passwd.passwd,
			        param->softap_passwd.passwd_len);
			ap_config.ap.password[param->softap_passwd.passwd_len] = '\0';
			esp_wifi_set_config(WIFI_IF_AP, &ap_config);
			BLUFI_INFO("Recv SOFTAP PASSWORD %s len = %d\n", ap_config.ap.password, param->softap_passwd.passwd_len);
			break;
		case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM:
			if (param->softap_max_conn_num.max_conn_num > 4)
			{
				return;
			}
			ap_config.ap.max_connection = param->softap_max_conn_num.max_conn_num;
			esp_wifi_set_config(WIFI_IF_AP, &ap_config);
			BLUFI_INFO("Recv SOFTAP MAX CONN NUM %d\n", ap_config.ap.max_connection);
			break;
		case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE:
			if (param->softap_auth_mode.auth_mode >= WIFI_AUTH_MAX)
			{
				return;
			}
			ap_config.ap.authmode = param->softap_auth_mode.auth_mode;
			esp_wifi_set_config(WIFI_IF_AP, &ap_config);
			BLUFI_INFO("Recv SOFTAP AUTH MODE %d\n", ap_config.ap.authmode);
			break;
		case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL:
			if (param->softap_channel.channel > 13)
			{
				return;
			}
			ap_config.ap.channel = param->softap_channel.channel;
			esp_wifi_set_config(WIFI_IF_AP, &ap_config);
			BLUFI_INFO("Recv SOFTAP CHANNEL %d\n", ap_config.ap.channel);
			break;
		case ESP_BLUFI_EVENT_GET_WIFI_LIST:
		{
			wifi_scan_config_t scanConf = {
					.ssid = NULL,
					.bssid = NULL,
					.channel = 0,
					.show_hidden = false
			};
			esp_wifi_scan_start(&scanConf, true);
			break;
		}
		case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
			BLUFI_INFO("Recv Custom Data %d\n", param->custom_data.data_len);
					esp_log_buffer_hex("Custom Data", param->custom_data.data, param->custom_data.data_len);
			break;
		default:
			break;
	}
}

int l_blufi_init(lua_State *L)
{
	GL = L;
	esp_err_t ret;

	initialise_wifi();

	ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

	esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
	ret = esp_bt_controller_init(&bt_cfg);
	if (ret)
	{
		BLUFI_ERROR("%s initialize bt controller failed: %s\n", __func__, esp_err_to_name(ret));
		lua_pushinteger(L,ret);
		return 1;
	}

	ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
	if (ret)
	{
		BLUFI_ERROR("%s enable bt controller failed: %s\n", __func__, esp_err_to_name(ret));
		lua_pushinteger(L,ret);
		return 1;
	}

	ret = esp_blufi_host_and_cb_init(&example_callbacks);
	if (ret)
	{
		BLUFI_ERROR("%s initialise failed: %s\n", __func__, esp_err_to_name(ret));
		lua_pushinteger(L,ret);
		return 1;
	}

	BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version());
	lua_pushinteger(L,ret);
	return 1;
}

int l_blufi_deinit(lua_State *L){
	esp_err_t ret;
	esp_blufi_deinit();
	esp_bluedroid_disable();
	esp_bluedroid_deinit();
	ret = esp_bt_controller_disable();
	if (ret)
	{
		BLUFI_ERROR("%s disable bt controller failed: %s\n", __func__, esp_err_to_name(ret));
		lua_pushinteger(L,ret);
		return 1;
	}
	ret = esp_bt_controller_deinit();
	if (ret)
	{
		BLUFI_ERROR("%s deinitialize bt controller failed: %s\n", __func__, esp_err_to_name(ret));
		lua_pushinteger(L,ret);
		return 1;
	}
	lua_pushinteger(L,ret);
	return 1;
}

int l_blufi_get(lua_State *L){
	lua_pushstring(L,(char *)sta_config.sta.ssid);
	printf("ssid: %s",(char *)sta_config.sta.ssid);
	lua_pushstring(L,(char *)sta_config.sta.password);
	printf("pw: %s",(char *)sta_config.sta.password);
	return 2;
}

#include "rotable.h"

static const rotable_Reg reg_blufi[] =
		{
				{"init", l_blufi_init, 0},
				{"deinit", l_blufi_deinit, 0},
				{"get", l_blufi_get, 0},
				{NULL, NULL,           0}};

LUAMOD_API int luaopen_blufi(lua_State *L)
{
	luat_newlib(L, reg_blufi);
	return 1;
}